﻿using System;

using System.Collections.Generic;
using System.Text;
using Microsoft.WindowsMobile.Status;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Threading;

namespace PFD
{
    class RemoteDataProvider
    {
        Thread tCyclic;
        Thread tOnChange;

        static MainForm mf;

        public static bool bStop = false;

        [StructLayout(LayoutKind.Sequential)]
        struct NavData
        {
            public double Airspeed;
            public double Heading;
            public double Altitude;
            public double Pitch;
            public double Roll;
            public double Latitude;
            public double Longitude;
        };

        [StructLayout(LayoutKind.Explicit, Size=48)]
        struct AutopilotData
        {
//            [MarshalAs(UnmanagedType.Bool)]
            [FieldOffset(0)]
            public bool master;
//            [MarshalAs(UnmanagedType.Bool)]
            [FieldOffset(4)]
            public bool flightdirector;
//            [MarshalAs(UnmanagedType.Bool)]
            [FieldOffset(8)]
            public bool autothrottle;
//            [MarshalAs(UnmanagedType.Bool)]
            [FieldOffset(12)]
            public bool glideslopeHold;
//            [MarshalAs(UnmanagedType.Bool)]
            [FieldOffset(16)]
            public bool approachHold;
//            [MarshalAs(UnmanagedType.Bool)]
            [FieldOffset(20)]
            public bool nav1Lock;
//            [MarshalAs(UnmanagedType.Bool)]
            [FieldOffset(24)]
            public bool airspeedHold;
//            [MarshalAs(UnmanagedType.I4)]
            [FieldOffset(28)]
            public int airspeedKnots;
//            [MarshalAs(UnmanagedType.Bool)]
            [FieldOffset(32)]
            public bool headingLock;
//            [MarshalAs(UnmanagedType.I4)]
            [FieldOffset(36)]
            public int headingDegrees;
//            [MarshalAs(UnmanagedType.Bool)]
            [FieldOffset(40)]
            public bool altitudeLock;
//            [MarshalAs(UnmanagedType.I4)]
            [FieldOffset(44)]
            public int altitudeFeet;
        };

        static byte[] StructureToByteArray(object obj)
        {
            int len = Marshal.SizeOf(obj);
            byte[] arr = new byte[len];
            IntPtr ptr = Marshal.AllocHGlobal(len);
            Marshal.StructureToPtr(obj, ptr, true);
            Marshal.Copy(ptr, arr, 0, len);
            Marshal.FreeHGlobal(ptr);
            return arr;
        }

        static void ByteArrayToStructure<T>(byte[] bytearray, ref T obj)
        {
            int len = Marshal.SizeOf(obj);
            IntPtr i = Marshal.AllocHGlobal(len);
            Marshal.Copy(bytearray, 0, i, len);
            obj = (T)Marshal.PtrToStructure(i, obj.GetType());
            Marshal.FreeHGlobal(i);
        }

        public delegate void doShit(string s, float v);
        public static doShit SetValue;

        public void setValues(string s, float v)
        {
            MainForm.sysVars.objVarsFloat[s].Value = v;
        }

        public static string getLocalIpAdress()
        {
            //  Get the local IP address
            IPHostEntry hostinfo = Dns.GetHostEntry(Dns.GetHostName());
#if Simulator
            return hostinfo.AddressList[0].ToString();
#else
            return "192.168.178.26";
#endif
        }

        public static void PerformWorkCyclic()
        {
            //  Create a socket for the multicast receive
            Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            //  It must be bound to the local interface at the multicast
            //  port
            IPAddress iplocaladdr = IPAddress.Parse(getLocalIpAdress());
            EndPoint iplocal = (EndPoint)new IPEndPoint(iplocaladdr, 11000);
            s.Bind(iplocal);

            //  Add the socket to the selected multicast group
            s.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(IPAddress.Parse("224.5.6.7"), iplocaladdr));

            EndPoint remoteEP = (EndPoint)new IPEndPoint(IPAddress.Any, 0);

            NavData nd = new NavData();
            try
            {
                while (!bStop)
                {
                    byte[] bytes = new byte[Marshal.SizeOf(nd)];
                    if (s.Poll(1000000, SelectMode.SelectRead))
                    {
                        s.ReceiveFrom(bytes, ref remoteEP);
                        ByteArrayToStructure(bytes, ref nd);
                        MainForm.sysVars.objVarsFloat["Altitude"].Value = (float)nd.Altitude;
                        MainForm.sysVars.objVarsFloat["Heading"].Value = (float)nd.Heading;
                        MainForm.sysVars.objVarsFloat["Airspeed"].Value = (float)nd.Airspeed;
                        MainForm.sysVars.objVarsFloat["PitchAngle"].Value = -(float)nd.Pitch;
                        MainForm.sysVars.objVarsFloat["RollAngle"].Value = -(float)nd.Roll;
                        MainForm.sysVars.objVarsFloat["Latitude"].Value = (float)nd.Latitude;
                        MainForm.sysVars.objVarsFloat["Longitude"].Value = (float)nd.Longitude;
                    }
                }
            }
            catch (Exception ex)
            {
                System.Windows.Forms.MessageBox.Show(ex.Message);
            }
        }

        public static void PerformWorkOnChange()
        {
            //  Create a socket for the multicast receive
            Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            //  It must be bound to the local interface at the multicast
            //  port
            IPAddress iplocaladdr = IPAddress.Parse(getLocalIpAdress());
            EndPoint iplocal = (EndPoint)new IPEndPoint(iplocaladdr, 11001);
            s.Bind(iplocal);

            //  Add the socket to the selected multicast group
            s.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(IPAddress.Parse("224.5.6.8"), iplocaladdr));

            EndPoint remoteEP = (EndPoint)new IPEndPoint(IPAddress.Any, 0);

            AutopilotData nd = new AutopilotData();
            try
            {
                while (!bStop)
                {
                    byte[] bytes = new byte[Marshal.SizeOf(typeof(AutopilotData))];
                    if (s.Poll(1000000, SelectMode.SelectRead))
                    {
                        s.ReceiveFrom(bytes, ref remoteEP);
                        ByteArrayToStructure(bytes, ref nd);
                        MainForm.sysVars.objVarsBool["AutopilotAirspeedLock"].Value = nd.airspeedHold;
                        MainForm.sysVars.objVarsFloat["AutopilotAirspeedVal"].Value = (float)nd.airspeedKnots;
                        MainForm.sysVars.objVarsFloat["AutopilotAltitudeVal"].Value = (float)nd.altitudeFeet;
                        MainForm.sysVars.objVarsBool["AutopilotAltitudeLock"].Value = nd.altitudeLock;
                        MainForm.sysVars.objVarsBool["AutopilotApproachLock"].Value = nd.approachHold;
                        MainForm.sysVars.objVarsBool["AutopilotAutothrottleEngage"].Value = nd.autothrottle;
                        MainForm.sysVars.objVarsBool["AutopilotFlightdirectorEngage"].Value = nd.flightdirector;
                        MainForm.sysVars.objVarsBool["AutopilotGlideslopeLock"].Value = nd.glideslopeHold;
                        MainForm.sysVars.objVarsFloat["AutopilotHeadingVal"].Value = (float)nd.headingDegrees;
                        MainForm.sysVars.objVarsBool["AutopilotHeadingLock"].Value = nd.headingLock;
                        MainForm.sysVars.objVarsBool["AutopilotMaster"].Value = nd.master;
                        MainForm.sysVars.objVarsBool["AutopilotNav1Lock"].Value = nd.nav1Lock;
                    }
                }
            }
            catch (Exception ex)
            {
                System.Windows.Forms.MessageBox.Show(ex.Message);
            }
        }

        public RemoteDataProvider(MainForm m)
        {
            mf = m;

            SetValue = new doShit(setValues);
        }

        public void Run()
        {
            tCyclic = new Thread(PerformWorkCyclic);
            tOnChange = new Thread(PerformWorkOnChange);

            tCyclic.IsBackground = true;
            tOnChange.IsBackground = true;

            bStop = false;
            tCyclic.Start();
            tOnChange.Start();
        }

        public void Cancel()
        {
            if (!bStop)
            {
                bStop = true;
                tCyclic.Join();
                tOnChange.Join();
                tCyclic.Abort();
                tOnChange.Abort();
            }
        }
    }
}
